/*
 * SCUT - A command line tool to manipulate shortcuts
 *
 * Copyright (c) 2004-2015 Malcolm Smith
 * malxau@users.sourceforge.net
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

//
//  For once, intentionally not WIN32_LEAN_AND_MEAN
//

#include <windows.h>
#include <shlobj.h>
#include <objidl.h>
#ifndef MINICRT
#include <stdio.h>
#include <stdlib.h>
#endif
#include <tchar.h>

#ifdef MINICRT
#include <minicrt.h>
#endif

typedef enum _operations {
    OPERATION_UNKNOWN=0,
    OPERATION_CREATE,
    OPERATION_MODIFY,
    OPERATION_EXEC,
    OPERATION_DUMP
} operations;

void usage(TCHAR * app)
{
    _tprintf(_T("%s -?\n"),app);
    _tprintf(_T("%s -create filename [-target target] [-args args] [-desc description]\n  [-hotkey hotkey] [-iconpath filename [-iconindex num]]\n  [-show showcmd] [-workingdir workingdir]\n"),app);
    _tprintf(_T("%s -modify filename [-target target] [-args args] [-desc description]\n  [-hotkey hotkey] [-iconpath filename [-iconindex num]]\n  [-show showcmd] [-workingdir workingdir]\n"),app);
    _tprintf(_T("%s -exec filename [-target target] [-args args] [-show showcmd]\n  [-workingdir workingdir]\n"),app);
    _tprintf(_T("%s -dump filename\n"),app);

    exit(EXIT_SUCCESS);
}

int _tmain(int argc, TCHAR * argv[])
{
    operations op         = OPERATION_UNKNOWN;
    TCHAR * szFile        = NULL;

    TCHAR * szArgs        = NULL;
    TCHAR * szDesc        = NULL;
    WORD    wHotkey       = (WORD)-1;
    TCHAR * szIcon        = NULL;
    WORD    wIcon;
    WORD    wShow         = (WORD)-1;
    TCHAR * szTarget      = NULL;    
    TCHAR * szWorkingDir  = NULL;
    TCHAR * app           = argv[0];
    IShellLink *scut      = NULL;
#ifndef UNICODE
    wchar_t szUniTarget[MAX_PATH];
#endif
    HRESULT hRes;
    BOOL bHandled;


    if (argc < 2 ||
        _tcscmp(argv[1], _T("/?")) == 0 ||
        _tcscmp(argv[1], _T("-?")) == 0 ||
        _tcscmp(argv[1], _T("--help")) == 0) {

        usage(app);

    }

    argc--;
    argv++;
    
    while (argc) {
        //
        // Every parameter needs an argument
        //
        if (argc == 1) {
            usage(app);
        }

        bHandled = FALSE;

        if (_tcscmp(argv[0], _T("-create")) == 0) {
            op = OPERATION_CREATE;
            szFile = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-modify")) == 0) {
            op = OPERATION_MODIFY;
            szFile = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-exec")) == 0) {
            op = OPERATION_EXEC;
            szFile = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-dump")) == 0) {
            op = OPERATION_DUMP;
            szFile = argv[1];
            bHandled = TRUE;
        }


        if (_tcscmp(argv[0], _T("-args")) == 0) {
            szArgs = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-desc")) == 0) {
            szDesc = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-hotkey")) == 0) {
            wHotkey = _ttoi(argv[1]);
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-iconpath")) == 0) {
            szIcon = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-iconindex")) == 0) {
            wIcon = _ttoi(argv[1]);
            bHandled = TRUE;
        }

        if (_tcscmp(argv[0], _T("-show")) == 0) {
            wShow = _ttoi(argv[1]);
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-target")) == 0) {
            szTarget = argv[1];
            bHandled = TRUE;
        }
        if (_tcscmp(argv[0], _T("-workingdir")) == 0) {
            szWorkingDir = argv[1];
            bHandled = TRUE;
        }

        if (!bHandled) {
            usage(app);
        }

        argv+=2;
        argc-=2;
    }

    if (op == OPERATION_UNKNOWN) {
        usage(app);
    }

    hRes = CoInitialize(NULL);
    if (!SUCCEEDED(hRes)) {
        _ftprintf( stderr, _T("CoInitialize failure: %x\n"), hRes);
        return EXIT_FAILURE;
    }

    IPersistFile *savedfile;
#ifndef UNICODE
    MultiByteToWideChar(CP_ACP, 0, szFile, -1, szUniTarget, MAX_PATH);
#endif

    hRes = CoCreateInstance(CLSID_ShellLink,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IShellLink,
        (void **)&scut);

    if (!SUCCEEDED(hRes)) {
        _ftprintf( stderr,
            _T("CoCreateInstance failure: %x\n"),
            hRes);
        return EXIT_FAILURE;
    }

    hRes = scut->QueryInterface(IID_IPersistFile, (void **)&savedfile);
    if (!SUCCEEDED(hRes)) {
        _ftprintf( stderr,
            _T("QueryInstance IPersistFile failure: %x\n"),
            hRes);
        return EXIT_FAILURE;
    }

    if (op == OPERATION_MODIFY ||
        op == OPERATION_EXEC ||
        op == OPERATION_DUMP) {
#ifndef UNICODE
        hRes = savedfile->Load(szUniTarget, TRUE);
#else
        hRes = savedfile->Load(szFile, TRUE);
#endif
        if (!SUCCEEDED(hRes)) {
            _ftprintf( stderr, _T("Load failure: %x\n"), hRes);
            return EXIT_FAILURE;
        }
    }

    if (op == OPERATION_DUMP) {
        TCHAR szTemp[MAX_PATH];
        int   wTemp;
        if (scut->GetPath(szTemp, MAX_PATH, NULL, 0) != NOERROR) {
            _ftprintf( stderr, _T("GetPath failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Target:          %s\n"), szTemp);
        if (scut->GetArguments(szTemp, MAX_PATH) != NOERROR) {
            _ftprintf( stderr, _T("GetArguments failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Arguments:       %s\n"), szTemp);
        if (scut->GetWorkingDirectory(szTemp, MAX_PATH) != NOERROR) {
            _ftprintf( stderr, _T("GetWorkingDirectory failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Working Dir:     %s\n"), szTemp);
        if (scut->GetDescription(szTemp, MAX_PATH) != NOERROR) {
            _ftprintf( stderr, _T("GetDescription failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Description:     %s\n"), szTemp);
        if (scut->GetIconLocation(szTemp, MAX_PATH, &wTemp) != NOERROR) {
            _ftprintf( stderr, _T("GetIconLocation failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Icon Path:       %s\n"), szTemp);
        _tprintf(_T("Icon Index:      %i\n"), wTemp);
        if (scut->GetShowCmd(&wTemp) != NOERROR) {
            _ftprintf( stderr, _T("GetShowCmd failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Show state:      %i\n"), wTemp);
        if (scut->GetHotkey(&wShow) != NOERROR) {
            _ftprintf( stderr, _T("GetHotkey failure\n"));
            return EXIT_FAILURE;
        }
        _tprintf(_T("Hotkey:          %i\n"), wShow);
        return EXIT_SUCCESS;
    }

    if (szTarget) {
        if (scut->SetPath(szTarget) != NOERROR) {
            _ftprintf( stderr, _T("SetPath failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (szArgs) {
        if (scut->SetArguments(szArgs) != NOERROR) {
            _ftprintf( stderr, _T("SetArguments failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (szDesc) {
        if (scut->SetDescription(szDesc) != NOERROR) {
            _ftprintf( stderr, _T("SetDescription failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (wHotkey != (WORD)-1) {
        if (scut->SetHotkey(wHotkey) != NOERROR) {
            _ftprintf( stderr, _T("SetHotkey failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (szIcon) {
        if (scut->SetIconLocation(szIcon, wIcon) != NOERROR) {
            _ftprintf( stderr, _T("SetIconLocation failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (wShow != (WORD)-1) {
        if (scut->SetShowCmd(wShow) != NOERROR) {
            _ftprintf( stderr, _T("SetShowCmd failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (szWorkingDir) {
        if (scut->SetWorkingDirectory(szWorkingDir) != NOERROR) {
            _ftprintf( stderr, _T("SetWorkingDirectory failure\n"));
            return EXIT_FAILURE;
        }
    }

    if (op == OPERATION_MODIFY ||
        op == OPERATION_CREATE) {
#ifndef UNICODE
        hRes = savedfile->Save(szUniTarget, TRUE);
#else
        hRes = savedfile->Save(szFile, TRUE);
#endif
        if (!SUCCEEDED(hRes)) {
            _ftprintf( stderr, _T("Save failure: %x\n"), hRes);
            return EXIT_FAILURE;
        }
    } else if (op == OPERATION_EXEC) {
        TCHAR szFile[MAX_PATH];
        TCHAR szArgs[MAX_PATH];
        TCHAR szDir[MAX_PATH];
        INT nShow;
        HINSTANCE hApp;
        if (scut->GetWorkingDirectory(szDir, MAX_PATH) != NOERROR) {
            _ftprintf( stderr, _T("GetWorkingDirectory failure\n"));
            return EXIT_FAILURE;
        }
        if (scut->GetArguments(szArgs,MAX_PATH) != NOERROR) {
            _ftprintf( stderr, _T("GetArguments failure\n"));
            return EXIT_FAILURE;
        }
        if (scut->GetPath(szFile, MAX_PATH, NULL, 0) != NOERROR) {
            _ftprintf( stderr, _T("GetPath failure\n"));
            return EXIT_FAILURE;
        }
        if (scut->GetShowCmd(&nShow) != NOERROR) {
            _ftprintf( stderr, _T("GetShowCmd failure\n"));
            return EXIT_FAILURE;
        }

        hApp = ShellExecute(NULL,
            NULL,
            szFile,
            szArgs,
            szDir,
            nShow);
        if ((UINT)hApp <= 32) {
            _ftprintf( stderr,
                _T("ShellExecute failure: %i\n"),
                hApp);
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

// vim:sw=4:ts=4:et:
